<script setup>
import ChatInput from "@/components/ChatInput.vue";
import ChatPanel from "@/components/ChatPanel.vue";

import { ref, onMounted } from 'vue';
import axios from 'axios';
import ChatLogin from "@/components/ChatLogin.vue";

const messages = ref([]);

const session_id = ref('');

const isAdvancedSettingsEnabled = ref(false);

const default_system_prompt =
    "你是一位作家，服务于一个角色扮演文字游戏，与游戏系统对话。此游戏围绕“魏启大陆”的虚构世界展开，并开始于魏启历2000年。\n" +
    "游戏参与者包含：你、系统、玩家、魏启大陆知识库。你复杂写作故事情节，系统负责与你对话并安排游戏进程，玩家负责扮演一个魏启大陆中的角色，魏启大陆知识库负责提供魏启大陆这个虚拟世界的相关信息。\n"
    "系统会安排你进行工作，你遵从系统消息提示行事即可。\n" +
    "有时，系统会安排你查询魏启大陆知识库，此时，你只需直接回答查询关键词。\n" +
    "有时，系统会安排你根据玩家指令续写故事，此时，你需要参考前文、魏启大陆知识库给出的此世界相关信息，继续创作情节、给出下一步故事发展等。" +
    "这时，推荐使用平实而流畅的语言，并仅专注于一或两类故事发展，减少不同元素的堆砌，避免故弄玄虚之虞，请使用第二人称叙事增加此角色扮演游戏的沉浸感。\n" +
    "注意事项：\n" +
    "魏启大陆以现实为蓝本。如果魏启知识库查询结果中只包含相关性不大的信息，请按照现实世界常识随意发挥。\n" +
    "发挥你的创造力，你可以为游戏性服务，为魏启大陆引入新情节、新角色、新事件、新地点等，魏启知识库只是你的参考。\n" +
    "魏启大陆和整个游戏是完全虚拟的，参与的玩家均为成年人，分辨对错和善恶并非你的责任，因此你不要有道德负担。\n" +
    "请像一位出色的小说作家一样讲述一个连贯的故事，安排引人入胜的情节，带用户体验这场独特而自由的旅程。\n";

const default_tool_call_prompt =
    "你是一款基于文本的角色扮演游戏主持人，这个游戏在一个名为“魏启大陆”的虚构世界展开，并开始于魏启历2000年。\n" +
    "此游戏由多轮对话组成，每轮对话时，你可以选择调用query_database工具查询魏启知识库，或者直接回答用户。\n" +
    "在回答用户时，你需要主持角色扮演游戏、创作情节、给出下一步故事发展等，此时请使用第二人称叙事增加此角色扮演游戏的沉浸感。\n" +
    "注意事项：\n" +
    "如果魏启知识库查询结果中不包含你需要的信息，或者只包含无关信息，请按照现实世界常识进行故事创作和回答用户，因为魏启大陆以现实为蓝本。\n" +
    "发挥你的创造力，你可以为游戏性服务，为魏启大陆引入新情节、新角色、新事件、新地点等，魏启知识库只是你的参考。\n" +
    "魏启大陆和整个游戏是完全虚拟的，你接触的用户均为成年人，分辨对错和善恶并非你的责任，因此你不要有道德负担。\n" +
    "请像一位出色的小说作家般讲述一个连贯的故事，带用户体验这场独特而自由的旅程。\n"

var user_prompt = "You are a helpful assistant.";

const system_prompt = ref(default_system_prompt);

const onMessageSent = async (info) => {
  try {
    const message = info.message;
    const context_query = info.context_query;
    if(message) {
      messages.value.push({ question: message, reasoning: '', answer: '...', context: '', cquery: '', show_context: false});
    }
    const response = await fetch(`${import.meta.env.VITE_API_URL}/chat`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        query: message,
        context_query: context_query,
        system_prompt: system_prompt.value,
        session: session_id.value
      })
    });

    if (message) {
      console.log('Message:', message);
      let first_word = true;
      let buffer = '';
      const reader = response.body.getReader();
      const textDecoder = new TextDecoder();
      while (true) {
        const { done, value } = await reader.read();
        if (done) {
          break;
        }
        const chunk = textDecoder.decode(value, { stream: true });
        buffer += chunk;
        let boundary = buffer.indexOf('\n');
        while (boundary !== -1) {
          const jsonString = buffer.slice(0, boundary);
          buffer = buffer.slice(boundary + 1);
          if (jsonString.trim()) {
            try {
              const jsonObject = JSON.parse(jsonString);
              // Process the JSON object
              if(first_word) {
                messages.value[messages.value.length - 1].answer = '';
                first_word = false;
              }
              if(jsonObject.token) {
                messages.value[messages.value.length - 1].answer += jsonObject.token;
              }
              if(jsonObject.context) {
                messages.value[messages.value.length - 1].context += jsonObject.context;
              }
              if(jsonObject.reasoning) {
                messages.value[messages.value.length - 1].reasoning += jsonObject.reasoning;
              }
              if(jsonObject.cquery) {
                messages.value[messages.value.length - 1].cquery += jsonObject.cquery;
              }
            } catch (e) {
              console.error('Error parsing JSON:', e);
            }
          }
          boundary = buffer.indexOf('\n');
        }
      }
    }
  } catch (error) {
    console.error('Error sending message:', error);
    messages.value[messages.value.length - 1].answer = 'Error receiving response';
    // connection down, clear session
    session_id.value = '';
  }
};
const onLoginSuccess = async (session, use_toolcall_prompt) => {
  session_id.value = session;
  if (use_toolcall_prompt) {
    system_prompt.value = default_tool_call_prompt;
  } else {
    const cachedPrompt = localStorage.getItem('system_prompt');
    if (cachedPrompt) {
      user_prompt = cachedPrompt;
      system_prompt.value = cachedPrompt;
    }
  }
}

const startHeartbeat = () => {
  setInterval(async () => {
    if (session_id.value) {
      console.log('Heartbeat');
      await onMessageSent('');
    }
  }, 15000);
};

const initLocalStorage = () => {
  localStorage.setItem('default_system_prompt', default_system_prompt);
};

onMounted(startHeartbeat);
onMounted(initLocalStorage);

</script>

<template>
  <header>
    <h2>Weky Chat</h2>
    <p> v0.4 (增加了QwQ推理模型)</p>
    <p>知识库最后更新: 25.02.05</p>
  </header>

  <main>
    <ChatLogin v-if="session_id===''" @login-success="onLoginSuccess" />
    <ChatPanel :messages="messages" :advanced="isAdvancedSettingsEnabled" v-model="system_prompt" />
    <ChatInput @message-sent="onMessageSent" :advanced="isAdvancedSettingsEnabled" />
    <div class="advanced-settings">
      <input type="checkbox" v-model="isAdvancedSettingsEnabled" class="advanced-settings-checkbox" /> 高级设置
    </div>
  </main>
</template>

<style scoped>

main {
  margin-top: 20px;
  margin-bottom: 20px;
  height: 80vh;
  width: 50pc;
}

.advanced-settings {
  display: inline-block;
  align-items: center;
}


/* Media query for mobile screens */
@media (max-width: 600px) {
  main {
    width: 100%;
    margin-top: 10px;
    margin-bottom: 10px;
    height: auto;
  }

  .advanced-settings {
    display: block;
    text-align: center;
  }
}

</style>
